/**
 * 
 */
package com.thor.palmmall.impl.elecharges;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;

import com.thor.commons.entity.OperateInfo;
import com.thor.commons.exception.ThorServiceException;
import com.thor.commons.query.QueryResult;
import com.thor.commons.query2.QueryDefinition2;
import com.thor.commons.util.Assert;
import com.thor.commons.util.PersistenceUtil;
import com.thor.palmmall.api.elecharges.EleChargesBill;
import com.thor.palmmall.api.elecharges.EleChargesBillService;
import com.thor.palmmall.api.elecharges.EleChargesBillState;
import com.thor.palmmall.api.elecharges.MeterComID;
import com.thor.palmmall.api.elecharges.PayChargesParam;
import com.thor.palmmall.api.elecharges.PayMethod;
import com.thor.palmmall.api.elecharges.UserEleDate;
import com.thor.palmmall.api.pay.alipay.AlipayService;
import com.thor.palmmall.api.pay.union.UnionPayService;
import com.thor.palmmall.api.pay.wechat.WeChatService;
import com.thor.palmmall.api.pay.wechat.entity.RsPaySignature;
import com.thor.palmmall.api.pay.wechat.entity.WechatUnifiedOrder;
import com.thor.palmmall.dao.elecharges.EleChargesBillDao;
import com.thor.palmmall.dao.elecharges.PEleChargesBill;
import com.thor.palmmall.dao.elecharges.converter.EleChargesBillConverter;
import com.thor.palmmall.rest.elecharges.APayMent;
import com.thor.palmmall.util.billnumber.BillNumberTool;
import com.thor.palmmall.util.pay.ContextHolderUtils;
import com.thor.palmmall.util.pay.IpUtil;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.DomDriver;

/**
 * @author NuoSa
 *
 * @since 1.0
 */
public class EleChargesBillServiceImpl implements EleChargesBillService {

  @Autowired
  private EleChargesBillDao eleChargesBillDao;
  @Autowired
  private BillNumberTool billNumberTool;
  @Autowired
  private WebServiceClient webServiceClient;
  @Autowired
  private AlipayService alipayService;
  @Autowired
  private WeChatService weChatService;

  @Override
  public UserEleDate getEleDateByUserId(String userId) throws ThorServiceException {
    UserEleDate eleDate = null;
    String date = "";
    try {
      date = webServiceClient.callGetDate(userId);
      eleDate = xmlToUserELeDate(date);
    } catch (Exception e) {
      throw new ThorServiceException("获取用户用电量失败！。" + date);
    }
    return eleDate;
  }

  @Override
  public String saveEleChargesBill(EleChargesBill eleChargesBill, OperateInfo operateInfo)
      throws ThorServiceException {
    Assert.assertArgumentNotNull(eleChargesBill, "eleChargesBill");
    Assert.assertArgumentNotNull(operateInfo, "operateInfo");
    PEleChargesBill ChargesBillToSave = null;
    if (eleChargesBill.getUuid() == null) {
      eleChargesBill
          .setBillNumber(billNumberTool.nextBillNumber(EleChargesBill.class.getSimpleName()));
      eleChargesBill.setState(EleChargesBillState.SUBMIT);
      ChargesBillToSave = EleChargesBillConverter.getInstance().convert(eleChargesBill);
    } else {
      EleChargesBill chargesBill = safeGet(eleChargesBill.getUuid(), eleChargesBill.getVersion());
      ChargesBillToSave = EleChargesBillConverter.getInstance().convert(eleChargesBill);
    }
    String uuid = eleChargesBillDao.saveEleChargesBill(ChargesBillToSave, operateInfo);
    return uuid;
  }

  @Override
  public EleChargesBill getEleChargesBill(String uuid) {
    Assert.assertArgumentNotNull(uuid, "uuid");
    return eleChargesBillDao.getEleChargesBill(uuid);
  }

  @Override
  public QueryResult<EleChargesBill> query(QueryDefinition2 definition) {
    Assert.assertArgumentNotNull(definition, "definition");
    return eleChargesBillDao.query(definition);
  }

  @Override
  public EleChargesBill getByBillNumber(String billNumber) {
    Assert.assertArgumentNotNull(billNumber, "billNumber");
    return eleChargesBillDao.getByBillNumber(billNumber);
  }

  @Override
  public APayMent payEleChargesBill(PayChargesParam param, OperateInfo operateInfo)
      throws ThorServiceException {
    EleChargesBill bill = eleChargesBillDao.getByBillNumber(param.getBillNumber());
    if (bill == null) {
      throw new ThorServiceException("找不到单号={0}的充值单。", param.getBillNumber());
    }
    if (bill.getState().equals(EleChargesBillState.PAID)
        || bill.getState().equals(EleChargesBillState.FINISH)) {
      throw new ThorServiceException("单号={0}的充值单。已经支付或已经完成", param.getBillNumber());
    }
    // 如果传入的金额和原充值单的金额不相同，则修改原重置单
    if (param.getAmount().compareTo(bill.getAmount()) != 0) {
      bill.setAmount(param.getAmount());
      bill.setMethod(param.getMethod());
      PEleChargesBill ChargesBillToSave = EleChargesBillConverter.getInstance().convert(bill);
      eleChargesBillDao.saveEleChargesBill(ChargesBillToSave, operateInfo);
    }
    EleChargesBill newbill = eleChargesBillDao.getByBillNumber(param.getBillNumber());
    APayMent payment = new APayMent();
    payment.setEleChargesBillNumber(newbill.getBillNumber());
    if (param.getMethod().equals(PayMethod.alipay)) {
      Map<String, String> alipayParam = createAlipayParam(newbill);
      String alipayPayment = alipayService.getTradePayRequest(alipayParam);
      payment.setAlipayPayment(alipayPayment);
    } else if (param.getMethod().equals(PayMethod.wechat)) {
      WechatUnifiedOrder request = createWeChatPayParam(newbill);
      RsPaySignature signature = weChatService.unifiedOrder(request);
      if (signature == null) {
        throw new ThorServiceException("微信支付凭证生成失败");
      }
      payment.setWxSignature(signature);
    } else if (param.getMethod().equals(PayMethod.unionpay)) {
      Map<String, String> params = createUnionPayParam(newbill);
      String unionPayTradeNo = unionPayService.getUnionPayTradeNo(params);
      payment.setUnionPayment(unionPayTradeNo);
    }
    return payment;
  }

  private UserEleDate xmlToUserELeDate(String xml) {
    XStream xstream = new XStream(new DomDriver());
    xstream.alias("dataset", UserEleDate.class);
    xstream.registerConverter(new Converter() {
      @Override
      public boolean canConvert(Class type) {
        return UserEleDate.class.equals(type);
      }

      @Override
      public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
        UserEleDate r = new UserEleDate();
        while (reader.hasMoreChildren()) {
          reader.moveDown();
          String nodeName = reader.getNodeName();
          if (!"parameter".equals(nodeName)) {
            continue;
          }
          String attrName = reader.getAttribute("name");
          if ("USEID".equals(attrName)) {
            r.setUserId(reader.getValue());
          } else if ("USERNAME".equals(attrName)) {
            r.setUserName(reader.getValue());
          } else if ("DATARQ".equals(attrName)) {
            try {
              r.setDataRq(new SimpleDateFormat("yyyy-MM-dd").parse(reader.getValue()));
            } catch (ParseException e) {
              e.printStackTrace();
            }
          } else if ("BALANCE".equals(attrName)) {
            r.setBalance(reader.getValue() == null ? null : new BigDecimal(reader.getValue()));
          } else if ("VALUE".equals(attrName)) {
            r.setValue(reader.getValue() == null ? null : new BigDecimal(reader.getValue()));
          } else if (attrName.startsWith("METERCOMID")) {
            MeterComID mc = new MeterComID();
            mc.setId(reader.getValue());
            r.getMeterComIds().add(mc);
          } else if (attrName.startsWith("VALUE")) {
            int index = Integer.valueOf(attrName.substring("VALUE".length()));
            r.getMeterComIds().get(index).setValue(reader.getValue());
          } else if (attrName.startsWith("RATE")) {
            int index = Integer.valueOf(attrName.substring("RATE".length()));
            r.getMeterComIds().get(index).setRate(reader.getValue());
          }
          reader.moveUp();
        }

        return r;
      }

      @Override
      public void marshal(Object source, HierarchicalStreamWriter writer,
          MarshallingContext context) {
      }
    });
    UserEleDate r = (UserEleDate) xstream.fromXML(xml);
    return r;
  }

  private EleChargesBill safeGet(String billUuid, long BillVersion) throws ThorServiceException {
    EleChargesBill chargesBill = eleChargesBillDao.getEleChargesBill(billUuid);
    if (chargesBill == null) {
      throw new ThorServiceException("找不到uuid={0}的充值单。", billUuid);
    }
    PersistenceUtil.checkVersion(BillVersion, chargesBill, "充值单", chargesBill.getBillNumber());
    return chargesBill;
  }

  public Map<String, String> createAlipayParam(EleChargesBill param) {
    Map<String, String> aliPayParam = new LinkedHashMap<String, String>();
    aliPayParam.put("body", "电费充值");
    aliPayParam.put("subject", "电费充值");
    aliPayParam.put("out_trade_no", param.getBillNumber());
    aliPayParam.put("total_amount", param.getAmount().toString());
    aliPayParam.put("product_code", "QUICK_MSECURITY_PAY");
    return aliPayParam;
  }

  public WechatUnifiedOrder createWeChatPayParam(EleChargesBill param) {
    WechatUnifiedOrder request = new WechatUnifiedOrder();
    request.setBody("电费充值");
    request.setDetail("电费充值");
    request.setGoods_tag("电费充值");
    request.setOut_trade_no(param.getBillNumber());
    request.setFee_type("CNY");
    request.setTotal_fee(param.getAmount().multiply(new BigDecimal(100.00)).intValue());
    request.setSpbill_create_ip(IpUtil.getIpAddr(ContextHolderUtils.getRequest()));
    return request;
  }

  @Override
  public void paynotify(String billNumber, String state, OperateInfo operateInfo)
      throws ThorServiceException {
    EleChargesBill bill = getByBillNumber(billNumber);
    if (bill == null) {
      throw new ThorServiceException("找不到billNumber={0}的充值单。", billNumber);
    }
    if ("PAID".equals(state)) {
      bill.setState(EleChargesBillState.PAID);
    } else {
      bill.setState(EleChargesBillState.FAILED);
    }
    this.saveEleChargesBill(bill, operateInfo);
  }

  private Map<String, String> createUnionPayParam(EleChargesBill param) {
    Map<String, String> unionPayParam = new LinkedHashMap<String, String>();
    unionPayParam.put("txnTime",
        new SimpleDateFormat("yyyyMMddHHmmss").format(new Date(System.currentTimeMillis())));
    unionPayParam.put("orderId", param.getBillNumber());
    unionPayParam.put("txnAmt", param.getAmount().multiply(new BigDecimal(100.00)).intValue() + "");
    return unionPayParam;
  }

  @Override
  public UserEleDate queryFreezeDataAndCT(String userId, Date date) throws ThorServiceException {
    UserEleDate eleDate = null;
    String data = "";
    try {
      data = webServiceClient.queryFreezeDataAndCT(userId, date);
      eleDate = xmlToUserELeDate(data);
    } catch (Exception e) {
      throw new ThorServiceException("获取用户用电量失败！。" + date);
    }
    return eleDate;
  }

  @Autowired
  UnionPayService unionPayService;

}
